/**
 * @file tiny_malloc.c
 * @brief 内存管理
 * @author 张恒宣 
 * @LastEditTime 2025/4/06
 * 
 * MIT License
 *
 * Copyright (c) 2025 张恒宣 luckkeymail@163.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*********************
 *      头文件
 *********************/

#include "tiny_malloc.h"
#include "tiny_def.h"
#include "tiny_rtos_context.h"

/**********************
 *      类型定义
 **********************/

#pragma pack(4)
typedef struct s_block
{
    tiny_uint32_t is_free;     /* 是否空闲 1 空闲 0 被使用, 保证内存对齐使用4byte. */
    tiny_uint32_t size;        /* 内存块大小, 不含block头. */
    struct s_block *next;      /* 下一个内存块 */
    struct s_block *prev;      /* 上一个内存块数据 */
    tiny_uint8_t data[4];      /* 内存块数据 4个字节让内存对齐 */
} block_t;
#pragma pack()

/**********************
 *       宏定义
 **********************/

#define BLOCK_HEAD_SIZE ((tiny_int64_t)(&(((block_t *)0)->data))) /* block头大小 */

/*********************
*     私有变量 
*********************/

static block_t *block_head = tiny_null;
static block_t *block_tail = tiny_null;
/* 堆栈内存空间. */
static tiny_uint8_t s_head_mem[TINY_HEAP_MAX_SIZE];

/*********************
*     私有函数 
*********************/

/// @brief 找到第一个大于等于size的内存块
/// @param block_head 内存块头
/// @param size 大小
/// @return 第一个大于等于size的内存块
static void *find_first_block(block_t *_block_head, tiny_uint32_t size)
{
    while (_block_head)
    {
        if (_block_head->is_free == 1 && _block_head->size >= size)
        {
            break;
        }
        _block_head = _block_head->next;
    }
    return _block_head;
}

/// @brief 分裂内存避免内存浪费
/// @param block 内存块
/// @param size 大小
static void split_mem(block_t *block, tiny_uint32_t size)
{
    block_t *pNext = (void *)(block->data + size);
    pNext->next = block->next;
    block->next->prev = pNext;
    block->next = pNext;
    pNext->prev = block;
    pNext->size = block->size - size - BLOCK_HEAD_SIZE;
    pNext->is_free = 1;
    block->size = size;
}

/// @brief 并内存避免内存碎片
/// @param block 内存块
/// @return 合并后的内存块
static void *merge_next_mem(block_t *block)
{
    block_t *pNext = block->next;
    block->size = block->size + pNext->size + BLOCK_HEAD_SIZE;
    block->next = pNext->next;
    if (pNext->next)
    {
        pNext->next->prev = block;
    }
    return block;
}

/// @brief 得到块头
/// @param ptr 内存指针
/// @return 块头
static block_t *get_ptr_block(void *ptr)
{
    return (void *)((tiny_uint8_t *)ptr - BLOCK_HEAD_SIZE);
}

/*********************
*     公共函数 
*********************/

/// @brief 申请内存
/// @param size 大小
/// @return 新申请的内存, 失败返回tiny_null
void *tiny_malloc(unsigned int size)
{
    void *pRetMem = tiny_null;
    tiny_uint32_t mem_size = ALIGN(size);
    int flag = disable_interrupt();
    if (block_head)
    {
        block_t *block = find_first_block(block_head, mem_size);
        if (block == tiny_null)
        {
            /* 扩充内存 */
            if ((tiny_uint64_t)block_tail >= (tiny_uint64_t)&s_head_mem[TINY_HEAP_MAX_SIZE - 1] - mem_size - BLOCK_HEAD_SIZE)
            {
                enable_interrupt(flag);
                return tiny_null;
            }
            block = (void *)(block_tail->data + block_tail->size);
            block->next = tiny_null;
            block->prev = block_tail;
            block_tail->next = block;
            block->size = mem_size;
            block_tail = block;
        }
        else if (mem_size + BLOCK_HEAD_SIZE + 8 < block->size)
        {
            /* 块内存分配过大，需要进行分裂. */
            split_mem(block, mem_size);
        }
        block->is_free = 0;
        pRetMem = block->data;
    }
    else
    {
        block_head = (block_t *)ALIGN((tiny_uint32_t)&s_head_mem[0]);
        block_head->is_free = 0;
        block_head->next = tiny_null;
        block_head->prev = tiny_null;
        block_head->size = mem_size;
        block_tail = block_head;
        pRetMem = block_head->data;
    }
    enable_interrupt(flag);
    return pRetMem;
}

/// @brief 释放内存
/// @param ptr 内存指针
void tiny_free(void *ptr)
{
    if (ptr == tiny_null)
    {
        return;
    }
    int flag = disable_interrupt();
    block_t *block = get_ptr_block(ptr);
    block->is_free = 1;
    /* 相邻有空闲内存需要合并, 保证下次分配块内存充足. */
    if (block->prev && block->prev->is_free)
    {
        block = merge_next_mem(block->prev);
    }
    if (block->next && block->next->is_free)
    {
        merge_next_mem(block);
    }
    enable_interrupt(flag);
}

/// @brief 内存设置
void tiny_memset(unsigned char *dst, unsigned char val, unsigned int size)
{
	if (!dst)
		return;
	
	while (size--)
	{
		*dst = val;
		dst++;
	}
}
